package br.com.infocruz.seam.crm.ejb.model;

import static org.apache.commons.lang.StringUtils.defaultString;

import java.io.Serializable;
import java.security.GeneralSecurityException;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.PrePersist;
import javax.persistence.PreUpdate;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.persistence.Version;

import org.hibernate.annotations.Type;
import org.hibernate.validator.Length;
import org.hibernate.validator.NotNull;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.security.management.PasswordSalt;
import org.jboss.seam.annotations.security.management.UserEnabled;
import org.jboss.seam.annotations.security.management.UserFirstName;
import org.jboss.seam.annotations.security.management.UserLastName;
import org.jboss.seam.annotations.security.management.UserPassword;
import org.jboss.seam.annotations.security.management.UserPrincipal;
import org.jboss.seam.annotations.security.management.UserRoles;
import org.jboss.seam.security.crypto.BinTools;
import org.jboss.seam.security.management.PasswordHash;

@Name("user")
@Entity
@Table(name="TB_USER")
@NamedQueries({
    @NamedQuery(name = "User.findByUserName", query = "select o from User as o where o.userName like :userName"),
    @NamedQuery(name = "User.countByUserName", query = "select count(o) from User as o where o.userName like :userName")
})
//@TypeDef(
//    name="encryptedString", 
//    typeClass=EncryptedStringType.class, 
//    parameters={@Parameter(name="encryptorRegisteredName",
//                           value="myHibernateStringEncryptor")}
//)
public class User extends AbstractEntity implements Serializable
{
    /**
	 * 
	 */
	private static final long serialVersionUID = -4886038842990603234L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name="ID", precision = 22, scale = 0)
	private Long id;
    
    @Column(name="VERSION")
    @Version
    private Integer version;
    
    @UserPrincipal
    @Column(name="USER_NAME", length = 20)
    @Length(max = 20)
    @NotNull
    private String userName;
    
    @UserFirstName
    @Column(name="FIRST_NAME", length = 40)
    @Length(max = 40)
    private String firstName;
    
    @UserLastName
    @Column(name="LAST_NAME", length = 25)
    @Length(max = 25)
    private String lastName;
    
    @OneToMany(mappedBy="user")    
	private Set<UserAuthorization> autorizacoes;
    
    @UserPassword
//    @Type(type="encryptedString")
    @Column(name="PASSWORD", length = 50)
    @Length(max = 50)
    private String password;

    @UserEnabled
    @Column(name="ENABLED")
    @Type(type="yes_no")
	private Boolean enabled;
    
    @UserRoles
    @OneToMany
    @JoinTable(name="TB_USER_ROLE", joinColumns={@JoinColumn(name="USER_ID", referencedColumnName="ID")}, inverseJoinColumns={@JoinColumn(name="ROLE_ID", referencedColumnName="ID")})
    private Set<Role> roles; 
    
    @Column(name = "PASSWORD_SALT", nullable = true, length = 50)
    @PasswordSalt
    private String salt;
    
    public User() {
	}
    
    @PrePersist
    @PreUpdate
    public void prePersist(){
        generateHashedPassword();
    }

	private void generateHashedPassword() {
		try {
			PasswordHash passwordHash = PasswordHash.instance();
			byte[] randomSalt = passwordHash.generateRandomSalt();
			this.salt =  BinTools.bin2hex(randomSalt);
			int iterations = 1000;
			this.password = passwordHash.createPasswordKey(password.toCharArray(), randomSalt, iterations);
		} catch (GeneralSecurityException e) {
			throw new RuntimeException(e);
		}
	}
    
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
    
    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }


    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    @Transient
    public String getFullName() {
    	return defaultString(getFirstName())+" "+defaultString(getLastName());
    }
    
	public Set<UserAuthorization> getAutorizacoes() {
		return autorizacoes;
	}

	public void setAutorizacoes(Set<UserAuthorization> autorizacoes) {
		this.autorizacoes = autorizacoes;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
	
	public Boolean isEnabled() {
		return enabled;
	}  	

	public void setEnabled(Boolean enabled) {
		this.enabled = enabled;
	}

	public Set<Role> getRoles() {
		return roles;
	}

	public void setRoles(Set<Role> roles) {
		this.roles = roles;
	}

	public String getSalt() {
		return salt;
	}

	public void setSalt(String salt) {
		this.salt = salt;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		User other = (User) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

}
